
#include <BRender>
#include <BViewer>
#include <BPainter>
#include <BKeyEvent>
#include <BMouseEvent>
#include <BStyle>
#include <BSystem>

#include "../Utilities.h"
#include "DrawTorusHandler.h"

DrawTorusHandler::DrawTorusHandler(BHandler* parent) : BHandler(parent)
{
	_vertexPicker = new AdsorbVertexPicker();
	_edgePicker = new EdgePicker();

	connect(this, Signal_AssetChanged, &DrawTorusHandler::slotAssetChanged);
	connect(this, Signal_PropertyChanged, &DrawTorusHandler::slotPropertyChanged);

}
DrawTorusHandler::~DrawTorusHandler()
{

}

bool DrawTorusHandler::collectEdges(const BVectorArray* vertices, const BElement* element, int index, EdgeArray& edges)
{
	edges.reset();
	const BIntArray* indices = element->indices();
	if (element->primitive() == Primitive_Triangles)
	{
		for (int i = 0; i < indices->size(); i += 3)
		{
			int ia = indices->at(i + 0);
			int ib = indices->at(i + 1);
			int ic = indices->at(i + 2);
			const BVector& va = vertices->at(ia);
			const BVector& vb = vertices->at(ib);
			const BVector& vc = vertices->at(ic);
			if (index == ia)
			{
				edges.append(FlatEdge(ia, ib, va, vb));
				edges.append(FlatEdge(ia, ic, va, vc));
				continue;
			}
			if (index == ib)
			{
				edges.append(FlatEdge(ib, ia, vb, va));
				edges.append(FlatEdge(ib, ic, vb, vc));
				continue;
			}
			if (index == ic)
			{
				edges.append(FlatEdge(ic, ia, vc, va));
				edges.append(FlatEdge(ic, ib, vc, vb));
				continue;
			}
		}
	}
	return edges.size();
}

void DrawTorusHandler::slotAssetChanged(BObject* object, const BValue& value)
{
	Target* target = value;
	this->setEnabled(target);
}
void DrawTorusHandler::slotPropertyChanged(BObject* object, const BValue& value)
{
	//if (Config* config = this->property("config"))
	//{
	//	_vertexPicker->setAdsorbPoint(config->adsorbPoint());
	//	_vertexPicker->setAdsorbLineMiddle(config->adsorbMiddle());
	//	_vertexPicker->setAdsorbPolygonCenter(config->adsorbCenter());
	//}
}

void DrawTorusHandler::attached(BViewer* viewer)
{
	_vertexPicker->setCamera(this->camera());
	_edgePicker->setCamera(this->camera());
	_hoveredIndex = -1;
	_vertices.clear();
}
void DrawTorusHandler::detached(BViewer* viewer)
{
	_torus = 0;
	_vertexPicker->setCamera(0);
	_edgePicker->setCamera(0);
	_vertices.clear();
	_hoveredIndex = -1;
}

void DrawTorusHandler::paint(BPainter& painter)
{
	if (_vertices.size())
	{
		painter.setColor(Color_Text);
		painter.drawText(this->viewer()->centerRect(), _vertices.size(), Align_LeftBottom);
	}
}
void DrawTorusHandler::render(BRender& render)
{
	render.setLighting(false);
	render.setDepthTest(false);

	if (_hoveredHit)
	{
		BVector point = _hoveredHit->absolutePoint();
		render.setPointSize(6);
		render.setPointSmooth(true);
		render.drawVertex(point);
	}

	{
		render.setPointSize(6);
		render.setPointSmooth(true);
		render.setColor(200, 0, 200);
		render.drawVertex(_point);
	}

	render.setDepthOffset(-1);
	render.setColor(200, 0, 0);
	render.drawVertices(_vertices);
	render.setDepthOffset(0);
}

void DrawTorusHandler::updateEvent(const BEvent& event)
{

}
void DrawTorusHandler::styleEvent(const BEvent& event)
{

}
void DrawTorusHandler::freshEvent(const BEvent& event)
{

}

void DrawTorusHandler::keyPressEvent(const BKeyEvent& keyEvent)
{
	if (keyEvent.key() == Key_Enter)
	{
		this->detach();
		keyEvent.accept();
		return;
	}
	if (keyEvent.key() == Key_Back)
	{
		_vertices.pop();
		this->emit(Signal_Changed);
		this->fresh();
		keyEvent.accept();
		return;
	}
	if (keyEvent.key() == Key_Delete)
	{
		this->fresh();
		keyEvent.accept();
		return;
	}
	if (keyEvent.key() == Key_Escape)
	{
		if (_torus)
		{
			Target* target = this->asset();
			if (BNode* node = target->node())
			{
				node->removeShape(_torus);
			}
			else if (BGroup* group = target->group())
			{
				group->remove(_torus);
			}
			else if (BScene* scene = target->scene())
			{
				scene->remove(_torus);
			}
		}
		this->detach();
		keyEvent.accept();
		return;
	}
}
void DrawTorusHandler::keyReleaseEvent(const BKeyEvent& keyEvent)
{

}

void DrawTorusHandler::mouseEnterEvent(const BMouseEvent& mouseEvent)
{

}
void DrawTorusHandler::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{

}
void DrawTorusHandler::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (_hoveredHit && _vertices.empty())
		{
			BVector point = _hoveredHit->absolutePoint();
			BVector normal = _hoveredHit->absoluteNormal();
			_vertices.append(point);
			_baseLine.set(point, point + normal);
			_basePlane.set(normal, point);
			_torus = new BTorus();
			Target* target = this->asset();
			if (BNode* node = target->node())
			{
				node->insertShape(_torus);
				BMatrix matrix = node->matrix().inverse();
				_torus->setPosition(point * matrix);
				_torus->setRotate(BQuater(BVector(0, 0, 1), normal * matrix));
			}
			else if (BGroup* group = target->group())
			{
				group->insert(_torus);
				_torus->setPosition(point);
				_torus->setRotate(BQuater(BVector(0, 0, 1), normal));
			}
			else if (BScene* scene = target->scene())
			{
				scene->insert(_torus);
				_torus->setPosition(point);
				_torus->setRotate(BQuater(BVector(0, 0, 1), normal));
			}
			_hoveredHit = 0;
			this->fresh();
			mouseEvent.accept();
			return;
		}
		_vertices.append(_point);
		this->fresh();
		mouseEvent.accept();
		return;
	}
}
void DrawTorusHandler::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		this->fresh();
		mouseEvent.accept();
		return;
	}
}
void DrawTorusHandler::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BCamera* camera = this->camera();
	const BPoint& pos = mouseEvent.pos();
	BLine line = this->viewer()->line(pos);
	if (mouseEvent.button() == Button_None)
	{
		if (_vertices.empty())
		{
			_vertexPicker->setLine(line);
			_hoveredHit = _vertexPicker->apply(this->scene());
			this->fresh();
		}
		else if (_vertices.size() == 1)
		{
			if (line.intersect(_basePlane, _point))
			{
				BReal outerRadius = (_point - _vertices[0]).length();
				_torus->setOuterRadius(outerRadius);
				this->fresh();
			}
		}
		else if (_vertices.size() == 2)
		{
			_point = BLine(_vertices[1], _vertices[1] + _basePlane.normal()).closest(line);
			BReal radius = (_point - _vertices[1]).length();
			_torus->setRadius(radius);
			this->fresh();
		}
		mouseEvent.accept();
		return;
	}
}
